package io.kiki.sba.registry.client.task;

import io.kiki.sba.registry.client.api.ClientConfig;
import io.kiki.sba.registry.client.api.Subscriber;
import io.kiki.sba.registry.client.provider.RegisterCache;
import io.kiki.sba.registry.client.provider.SubscriberImpl;
import io.kiki.sba.registry.client.remoting.Client;
import io.kiki.sba.registry.core.model.SyncConfigRequest;
import io.kiki.sba.registry.core.model.SyncConfigResponse;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Setter
@Getter
public class SyncConfigThread extends Thread {
    private static final Logger logger = LoggerFactory.getLogger(SyncConfigThread.class);

    private Client client;

    private RegisterCache registerCache;

    private ClientConfig clientConfig;

    private ObserverHandler observerHandler;

    public SyncConfigThread(Client client, RegisterCache registerCache, ClientConfig clientConfig, ObserverHandler observerHandler) {
        super("SyncConfigThread");
        this.setDaemon(true);
        this.client = client;
        this.registerCache = registerCache;
        this.clientConfig = clientConfig;
        this.observerHandler = observerHandler;
    }

    @Override
    public void run() {
        int retryInterval = clientConfig.getSyncConfigRetryInterval();
        //noinspection InfiniteLoopStatement
        while (true) {
            try {
                Thread.sleep(retryInterval);

                if (!client.isConnected()) {
                    continue;
                }

                SyncConfigRequest request = new SyncConfigRequest();
                request.setDataCenter(clientConfig.getDataCenter());
                request.setZone(clientConfig.getZone());
                Object result = client.invokeSync(request);

                if (!(result instanceof SyncConfigResponse)) {
                    logger.warn("[syncConfig] unknown response type, {}", result);
                    continue;
                }

                SyncConfigResponse response = (SyncConfigResponse) result;
                if (!response.isSuccess()) {
                    logger.warn("[syncConfig] request failed, {}", response);
                    continue;
                }

                int interval = response.getRetryInterval();
                retryInterval = Math.max(retryInterval, interval);

                List<String> availableSegments = response.getAvailableSegments();

                List<Subscriber> subscriberList = registerCache.getSubscriberList();
                for (Subscriber subscriber : subscriberList) {
                    try {
                        if (!(subscriber instanceof SubscriberImpl)) {
                            continue;
                        }

                        SubscriberImpl subscriberImpl = (SubscriberImpl) subscriber;

                        if (!subscriberImpl.isInited()) {
                            logger.info("[syncConfig] DefaultSubscriber not init, {}", subscriberImpl.getRegistId());
                            continue;
                        }
                        List<String> nowAvailableSegments = subscriberImpl.getAvailableSegments();

                        if (isEqualCollections(availableSegments, nowAvailableSegments)) {
                            continue;
                        }

                        subscriberImpl.setAvailableSegments(availableSegments);

                        observerHandler.notify(subscriberImpl);
                    } catch (Exception e) {
                        logger.error("[syncConfig] try notify subscriber error, registId: {}, availableSegments: {}", subscriber.getRegistId(), availableSegments, e);
                    }
                }
            } catch (Throwable e) {
                logger.error("[syncConfig] sync config error, retryInterval: {}", retryInterval, e);
            }
        }
    }

    private boolean isEqualCollections(Collection<String> a, Collection<String> b) {
        if (null == a) {
            a = new ArrayList<String>();
        }

        if (null == b) {
            b = new ArrayList<String>();
        }

        return a.size() == b.size() && a.equals(b);
    }
}
